import os
import warnings

import numpy as np
from PIL import Image
from sklearn import metrics
from sklearn.decomposition import PCA  # 降维
from sklearn.linear_model import LogisticRegression  # 逻辑回归
from sklearn.model_selection import train_test_split,GridSearchCV
from sklearn.neighbors import KNeighborsClassifier
from sklearn.preprocessing import scale  # 缩放， 归一
from sklearn.tree import DecisionTreeClassifier # 决策树

warnings.filterwarnings('ignore')


class MachineLearn():
    def __init__(self, pca_k, file_path):
        # 1.构造数据
        # todo:训练集
        BASE_PATH = os.path.abspath(os.path.dirname(__file__)) + os.sep
        PATH = BASE_PATH + "att_faces" + os.sep
        x = []
        y = []
        # 将照片导入numpy数组，然后将他们的像素矩阵替换为向量
        for dir_path, dir_names, file_names in os.walk(PATH):
            for fn in file_names:
                # if fn.endswith("pgm"):
                if fn[-3:] == "pgm":
                    image_filename = os.path.join(dir_path, fn)
                    # 图像处理
                    img = Image.open(image_filename).convert("L")  # 灰化
                    im_arr = np.array(img)
                    # print(im_arr.shape)  #112*92=10304
                    # 拉伸成一维数组， 且归一化操作
                    # x_d = im_arr.reshape(10304).astype("float32")/255 #数字归一
                    x_d = scale(im_arr.reshape(10304).astype("float32"))
                    x.append(x_d)
                    y.append(dir_path)

        # print(y)
        x = np.array(x)
        # 训练集和测试集的分离
        self.x_train, self.x_test, self.y_train, self.y_test = train_test_split(x, y, test_size=0.25, random_state=7)
        # todo:pca降维
        pca = PCA(n_components=pca_k)
        self.x_train = pca.fit_transform(self.x_train)
        self.x_test = pca.transform(self.x_test)
        # print(self.x_train.shape)
        # print(self.x_test.shape)
        print(self.y_train)

        # 处理标签集  输出y
        yy_train = []
        # C:\\Users\\lijun\\Desktop\\yq_project\\ai_yueqian\\face\\att_faces\\s24
        for i in range(len(self.y_train)):
            # s24 字符串  --》24
            yy = int(str(self.y_train[i].split('\\')[-1])[1:])  # 只保留数字
            # print(yy)
            yy_train.append(yy)
        self.y_train = np.array(yy_train).reshape(-1, 1)
        yy_test = []
        for i in range(len(self.y_test)):
            yy = int(str(self.y_test[i].split('\\')[-1])[1:])
            yy_test.append(yy)
        self.y_test = np.array(yy_test).reshape(-1, 1)
        # print(self.y_train[:5])
        # print(self.y_test[:5])

        # todo：把输入的图片处理及优化
        im = Image.open(file_path).convert("L")  # 灰度化
        im_arr = np.array(im)
        # pre_face = scale(im_arr.reshape(10304).astype("float32"))
        pre_face = scale(im_arr.reshape(-1).astype("float32"))  # 降维，归一
        x_pred = [pre_face]
        x_pred = np.array(x_pred)
        self.pred = pca.transform(x_pred)  # 降维处理

    def knn(self):
        """K近邻算法 实现人脸预测"""
        # 2、构造模型
        model = KNeighborsClassifier(n_neighbors=1)
        # 3、模型训练
        model.fit(self.x_train, self.y_train)
        # 4、模型预测
        pred = model.predict(self.pred)[0]
        pred = 'KNN预测：' + 's' + str(pred)
        # 5、模型评估
        pred_test_y = model.predict(self.x_test)
        metr = round(metrics.accuracy_score(self.y_test, pred_test_y), 2)
        metr = "正确率：" + str(metr)
        return pred, metr

    def logsticRegressor(self):
        """逻辑回归算法"""
        # 2、构造模型
        # l-bfgs:共轭梯度法，还有其他三种算法：liblinear,newton-cg,sag
        # 分类选择："ovr","multinomial",todo:multinomal一般用在多元逻辑回归上
        model = LogisticRegression(multi_class="multinomial", solver="lbfgs")
        # 3、模型训练
        model.fit(self.x_train, self.y_train)
        # 4、模型预测
        pred = model.predict(self.pred)[0]
        pred = 'Log逻辑回归预测：' + 's' + str(pred)
        # 5、模型评估
        pred_test_y = model.predict(self.x_test)
        metr = round(metrics.accuracy_score(self.y_test, pred_test_y), 2)
        metr = "正确率：" + str(metr)
        return pred, metr
    def decisionTree(self):
        """决策树实现人脸预测"""
        #todo 模型调参
        # es = DecisionTreeClassifier()
        # para = {"criterion": ["gini", "entropy"], "max_depth": range(1, 50)}
        # scoring_func = metrics.make_scorer(metrics.accuracy_score)
        # grid = GridSearchCV(estimator=es, param_grid=para, scoring=scoring_func)
        # grid.fit(self.x_train, self.y_train)
        # acc = grid.best_score_
        # print(f"最优正确率为：{acc}") #
        # reg = grid.best_estimator_
        # print(f"最优参数为：\n", reg)  #criterion='gini' max_depth=39


        # 2.构建模型
        model = DecisionTreeClassifier(criterion='gini', max_depth=39)
        # 3、模型训练
        model.fit(self.x_train, self.y_train)
        # 4、模型预测
        pred = model.predict(self.pred)[0]
        pred = 'Dec决策树预测：' + 's' + str(pred)
        # 5、模型评估
        pred_test_y = model.predict(self.x_test)
        metr = round(metrics.accuracy_score(self.y_test, pred_test_y), 2)
        metr = "正确率：" + str(metr)
        return pred, metr


if __name__ == '__main__':
    ml = MachineLearn(150, '9.pgm')
    # print(ml.knn())
    # print(ml.logsticRegressor())
    print(ml.decisionTree())
